#! /bin/sh
# -----------------------------------------------------------------------------
#     Title: A script to invoke the various CL implementations in a uniform way
#   Created: 1999-01-16 16:41
#    Author: Gilbert Baumann <unk6@rz.uni-karlsruhe.de>
#   License: LGPL (See file GNU-LGPL for details)
# -----------------------------------------------------------------------------
#  (c) copyright 1999 by Gilbert Baumann, Sam Steingold, Bruno Haible
# $Id: maxima-run-lisp,v 1.7 2003-03-05 01:36:25 rtoy Exp $
# $Source: /home/lbutler/maxima/sandbox/cvs/maxima/maxima/lisp-utils/maxima-run-lisp,v $

# Prior to invocation the LISPTYPE environment variable must been set to
# one of these:
#
#    clisp      uses ${CLISP} if set, else "clisp"
#    cmucl      uses ${CMUCL} if set, else "lisp"
#    acl43      uses ${ACL43} if set, else "cl"
#    acl5       uses ${ACL5} if set, else "cl"
#    acl6       uses $(ACL6) if set, else "alisp    
#    gcl        uses ${GCL} if set, else "gcl"
#    sbcl       uses ${SBCL} if set, else "sbcl"
#    openmcl    uses ${OPENMCL} if set, else "openmcl"

usage(){
    cat <<\EOF
Usage:
 run-lisp [clause ...] [argument ...]
     clause ::= -i file    ; load file `file'
              | -c file    ; compile file `file'
              | -x form    ; execute form `form'
              | -I image   ; use image file `image'
              | -d image   ; dump to image `image'
	      | -f initfun ; set init function of dumped image to `initfun'
	      | --extra-args=args ; add 'args' to lisp command line
              | --safety n ; set safety level for compilation
              | --speed n  ; set speed level for compilation

     Note: Lisp specific extensions (e.g. .dxl on ACL or .mem for CLISP)
           are not to be included into the image argument

     Anything else is stuffed into the Lisp variable 'argv', which is
     available in the lexical environment forms given to -x are
     evaluated in.

 run-lisp [--extra-args=args] -run image [-i file]
   interactively run 'image'
   (user:run) will be called upon start up.

Makefile support

 run-lisp -faslext
   echo the fasload file extension to stdout.
   This is useful for Makefile; you could then say e.g.
   FAS:=$(shell $(TOP)/bin/run-lisp -faslext)

 run-lisp -dumpext
   echo the memory image extension _with_ dot.
   usage: (like above)
   DUMP:=$(shell $(TOP)/bin/run-lisp -dumpext)

 run-lisp -cat [fasl-file ...]
   Cat all the given fasl files together.

EXAMPLE

   $ run-lisp -x "(print argv)" foo bar baz "Hallo Welt" foo\"bar
   ("foo" "bar" "baz" "Hallo Welt" "foo\"bar")
   $
EOF
    exit 0;
}

fail(){
    echo "$0: $*" 1>&2
    exit 1
}

case "$1" in
  --extra-args=*)
		    extra_args=`echo $1 | sed 's/--extra-args=//'`
		    shift 1
		    ;;
  *)
   ;;
esac

case "$1" in
  "--help" | "-h" | "-?" )
      usage
      ;;
  "-cat" )
      shift
      case "$LISPTYPE" in
        clisp | cmucl | acl43 | acl[56] )
            cat "$@"
            ;;
        * )
            fail "Sorry, option -cat not supported for LISPTYPE=${LISPTYPE}."
            ;;
      esac
      ;;
  "-faslext" )
      shift
      case "$LISPTYPE" in
        clisp )
            # This is (pathname-type (car (system::*compiled-file-types*))).
            echo 'fas'
            ;;
        cmucl )
            # This can be found via
            #    (c:backend-fasl-file-type c:*target-backend*),
            # but for speed we look at the uname first.
            case `uname -m 2>/dev/null` in
                i[3-6]86 )
                   echo 'x86f'
                   ;;
                * )
                  # Call .
                  ${CMUCL-lisp} -noinit \
                         -eval "(progn \
                                  (write-string (c:backend-fasl-file-type c:*target-backend*))
                                  (terpri)
                                  (ext:quit 0))"
                  ;;
            esac
            ;;
        acl43 | acl[56] )
            echo 'fasl'
            ;;
        gcl )
            echo 'o' # but also 'data' on same platforms
            ;;
        sbcl )
            echo 'fasl' # How about other platforms?
            ;;
	openmcl )
	      case `uname -s` in
		  Darwin )        # Darwin
		      echo 'dfsl'
		      ;;
		  * )             # Linux PPC
		      echo 'pfsl'
		      ;;
	      esac
	      ;;
        * )
            # Since make does not stop when the exit status is 1, we simply
            # echo LISPTYPE_NOT_SET here.
            echo LISPTYPE_NOT_SET
            fail "Sorry, option -faslext not supported for LISPTYPE=${LISPTYPE}."
            ;;
      esac
      ;;
  "-dumpext" )
      shift 1
      case "$LISPTYPE" in
          clisp)
              echo .mem
          ;;
          acl43)
              echo ""
          ;;
          acl[56])
              echo .dxl
          ;;
          cmucl)
              echo .core
          ;;
          sbcl)
              echo .core # This is just a guess
          ;;
	  openmcl)
	      echo .image
	  ;;
          *)
              # Since make does not stop when the exit status is 1, we simply
              # echo LISPTYPE_NOT_SET here.
              echo LISPTYPE_NOT_SET
              fail "Sorry, option -dumpext not supported for LISPTYPE=${LISPTYPE}."
          ;;
      esac
    ;;
  "-run" )
    # we special case on '-run' for now
    shift 1
    if [ x"$2" = x"-i" ]; then
      load_file=$3
    fi
    case "$LISPTYPE" in
        clisp)
	    if [ -n "$load_file" ]; then
	      load_arg="-i $load_file"
	    fi
            ${CLISP-clisp} $extra_args $load_arg -M "$1".mem 
	    ;;
        cmucl )
	    if [ -n "$load_file" ]; then
	      load_arg="-load $load_file"
	    fi
	    # jfa: fixme 04/24/02
	    # jfa: The -eval part of the following line is a big hack!
            ${CMUCL-lisp} -eval "(user::run)" $extra_args $load_arg -core "$1".core
            ;;
        sbcl )
	    if [ -n "$load_file" ]; then
	      load_arg="--load $load_file"
	    fi
	    # jfa: fixme 04/24/02
	    # jfa: The --eval part of the following line is a big hack!
            ${SBCL-sbcl}  --core "$1".core --eval "(user::run)" $extra_args $load_arg
            ;;
        acl43 )
	    if [ -n "$load_file" ]; then
	      load_arg="-i $load_file"
	    fi
            # Why "$1"? ACL4.3 dumps executables
            "$1" $extra_args $load_arg -e '(unwind-protect (run) (excl:exit))'
            ;;
        acl5 )
	    if [ -n "$load_file" ]; then
	      load_arg="-i $load_file"
	    fi
            ${ACL5-cl} $extra_args $load_arg -I "$1".dxl -e '(unwind-protect (run) (excl:exit))'
            ;;
        acl6 )
	    if [ -n "$load_file" ]; then
	      load_arg="-i $load_file"
	    fi

	    # I (RLT) am not sure the -e is quite right, but this
	    # allows us to get back to lisp when the user does
	    # to_lisp(), like the way CMUCL does it.

	    ${ACL6-alisp} $extra_args $load_arg -I "$1".dxl -e '(user::run)'
            ;;
        gcl )
	    if [ -n "$load_file" ]; then
	      load_arg="-load $load_file"
	    fi
            "$1" $extra_args $load_arg -eval '(run)'
            ;;
	openmcl )
	    if [ -n "$load_file" ]; then
		load_arg="-l $load_file"
	    fi
	    ${OPENMCL-openmcl} -I "$1.image" $load_arg
	    ;;
        *)
            fail "Sorry, option -run not supported for LISPTYPE=${LISPTYPE}."
        ;;
    esac
    ;;
  * )
    # Multiple arguments.
    unset image
    todo=""             # list of forms to execute
    args=""             # list of arguments (strings) to pass
    while [ $# != 0 ]; do
      case "$1" in
        -i)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            todo=$todo" (load \"${arg}\")"
            shift
            ;;
        -x)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            todo=$todo" $1"
            shift
            ;;
        -c)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            # todo=$todo" (compile-file \"${arg}\" :print nil)"
            # truename helps, when using Franz' emacs interface
            todo=$todo" (compile-file (truename \"${arg}\") :print nil)"
            shift
            ;;
        -I)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            image="$1"
            shift
            ;;
        -f)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            initfun=$1
            case "$LISPTYPE" in
              clisp )
                  initfun_arg=":init-function #'$initfun"
                  ;;
              cmucl )
                  initfun_arg=":init-function #'$initfun"
                  ;;
              sbcl )
                  initfun_arg=":toplevel #'$initfun"
                  ;;
              gcl )
                  todo=$todo" (setq si::*top-level-hook* #'$initfun)"
                  ;;
	      openmcl )
		  initfun_arg=":toplevel-function #'$initfun"
		  ;;
              * )
                  fail "Sorry, option -d not supported for LISPTYPE=${LISPTYPE}."
                  ;;
            esac
            shift
            ;;
        -d)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            case "$LISPTYPE" in
              clisp )
                  todo=$todo" (#+lisp=cl ext:saveinitmem #-lisp=cl lisp:saveinitmem \"${arg}.mem\" $initfun_arg)"
                  ;;
              cmucl )
                  todo=$todo" (ext:save-lisp \"${arg}.core\" $initfun_arg)"
                  ;;
              sbcl )
                  todo=$todo" (sb-ext:save-lisp-and-die \"${arg}.core\" $initfun_arg)"
                  ;;
              acl43 )
                  todo=$todo" (excl:dumplisp :name \"${arg}\")"
                  ;;
              acl[56] )
                  todo=$todo" (excl:dumplisp :name \"${arg}.dxl\")"
                  ;;
              gcl )
                  todo=$todo" (si:save-system \"${arg}\")"
                  ;;
	      openmcl )
		  todo=$todo" (ccl:save-application \"${arg}.image\" $initfun_arg)"
		  ;;
              * )
                  fail "Sorry, option -d not supported for LISPTYPE=${LISPTYPE}."
                  ;;
            esac
            shift
            ;;
        --safety)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift 1
            todo=$todo" (proclaim (quote (optimize (safety "$1"))))"
            shift 1
        ;;
        --speed)
            if [ $# = 1 ]; then
              fail "missing argument for $1"
            fi
            shift 1
            todo=$todo" (proclaim (quote (optimize (speed "$1"))))"
            shift 1
        ;;
        *)
            backslashify='s,\(["\\]\),\\\1,g'
            arg=`echo "$1" | sed -e "$backslashify"`
            args=$args" \"${arg}\""
            shift
            ;;
      esac
    done

    # done with collecting the arguments

    if [ x"$LISPTYPE" = x"sbcl" ]; then
      todo="(progn${todo}(sb-ext:quit))"
    elif [ x"$LISPTYPE" = x"openmcl" ]; then
      todo="(progn ${todo} (ccl::quit))"
    else
      todo="(progn${todo})"
    fi
    args="(${args})"
    todo="(let ((argv '$args)) (declare (ignorable argv)) $todo (values))"

    case "$LISPTYPE" in
      clisp )
          todo="(progn (setq #+lisp=cl ext:*load-paths* #-lisp=cl lisp:*load-paths* '(#P\"\")) ${todo})"
          test -z "$image" || image="-M ${image}.mem";
          echo ${CLISP-clisp} -norc -q ${image} -x "$todo"
          exec ${CLISP-clisp} $extra_args -norc -q ${image} -x "$todo"
          ;;
      cmucl )
          # we have to convince CMUCL to return a proper exit status.
          todo="(let (.res (.cond t))
                  (unwind-protect
                    (multiple-value-setq (.res .cond)
                       (ignore-errors (progn $todo
                                        (fresh-line) (finish-output))))
                    (unix:unix-exit (if .cond 1 0))))"
          test -z "$image" || image="-core ${image}.core";
	  exec ${CMUCL-lisp} $extra_args -noinit ${image} -eval "$todo"
          ;;
      acl43 )
          exec echo "$todo" | ${image-${ACL43-cl}} $extra_args -batch
          ;;
      acl5 )
          test -z "$image" || image="-I ${image}.dxl";
          exec echo "$todo" | ${ACL5-cl} ${image+"-I ${image}.dxl"} $extra_args -batch
          ;;
      acl6 )
          test -z "$image" || image="-I ${image}.dxl";
          exec echo "$todo" | ${ACL6-alisp} ${image+"-I ${image}.dxl"} $extra_args -batch
          ;;
      gcl )
	    echo ${image-${GCL-gcl}} $extra_args -batch -eval "$todo"
          ${image-${GCL-gcl}} $extra_args -batch -eval "$todo"
	  ;;
      sbcl )
          ${image-${SBCL-sbcl}} $extra_args --noinform --noprint --eval "$todo"
          ;;
      openmcl )
	  test -z "$image" || image="-I ${image}.image";
	  echo ${OPENMCL-openmcl} ${image} -e "${todo}" $extra_args
	  ${OPENMCL-openmcl} ${image} -e "${todo}" $extra_args
	  ;;
      * )
          if [ -n "$LISPTYPE" ] ; then
            fail "Sorry, LISPTYPE=${LISPTYPE} is not supported"
          else
            fail "LISPTYPE environment variable is not set"
          fi
          ;;
    esac

esac
